//import chalk from 'chalk' //import log from 'loglevel' //import prefix from 'loglevel-plugin-prefix' //import lf from 'localforage' //import 'moment' /* const colors = { TRACE: chalk.magenta, DEBUG: chalk.cyan, INFO: chalk.blue, WARN: chalk.yellow, ERROR: chalk.red, }; prefix.reg(log); log.enableAll(); prefix.apply(log, { format(level, name, timestamp) { return `${chalk.gray(`[${timestamp}]`)} ${colors[level.toUpperCase()](level)} ${chalk.green(`${name}:`)}`; }, }); prefix.apply(log.getLogger('critical'), { format(level, name, timestamp) { return chalk.red.bold(`[${timestamp}] ${level} ${name}:`); }, });// window.log = log */ //export const locale = moment.locale('uk') //window.moment = moment // - ftp: make localForage available as window global import * as localForage from "../js/localforage.min.js" window.lf = localforage //const lf = {createInstance: function (a) {console.log(a)}} /* This should not be loaded from Here No code here uses it. The hub is plugged out via a base tag in the context of the app thata uses it! */ export const mindgraph = { archive: archiveModule(), dot: dotModule(), log: linkogModule(), lf: localForageModule(), //mark: markModule(), linkog: linkogModule(), //- ftp, create : Storage Module in MindGraph storage: storageModule(), utils: utilsModule(), viewHelpers: viewHelpers(), transform: transformModule() } //- ftp : add mindgraph to window as mg4 //- ftp alias mindgraph as mg4 window.mg4 = mindgraph //- ftp : add mindgraph to window as mindgraph // as my Root Capability Context Object //- ftp : make mindgraph global window.mindgraph = mindgraph //-ftp set version for mg4 mg4.version = "4.1010" //- ftp : abbreviated references to capabilities //- ftp : abbreviate aliases m.d m.u window.m = window.m || mindgraph m.d = m.dot m.u = m.utils // - ftp : make moment global //window.moment = moment /** * * - module : log */ function logModule() { /** * Intialize module */ function init() { alert("init log") } return { init } } /** * Working with Dots */ function dotModule(store) { let DOT, map, n let record = true; function shimLocalStorage() { return { save: function (n) { localStorage[n.a] = JSON.stringify(n) }, getNodeItem: function (a) { let n = null; let data = localStorage[a] if (data) { n = JSON.parse(data); } return n; }, setupMap: function () { // console.log("Setting up map"); map = {}; var values = []; for (var i = 0; i < localStorage.length; i++) { var key = localStorage.key(i); var item = localStorage.getItem(key); var value = item; try { value = JSON.parse(item); } // The saved item may not be JSON catch (e) { } if (value.a === key) { map[key] = value; values.push(value); } } map = { map: map, values: values }; return map; }, setFocus: function (a) { if (a.a) { a = a.a } localStorage.CN = a; }, getFocus: function () { let a = localStorage.CN if (a) { return getNodeItem(a) } } } } store = store || shimLocalStorage() //- ftp : transfer store shim const setupMap = store.setupMap const setFocus = store.setFocus const getFocus = store.getFocus const getNodeItem = store.getNodeItem const save = store.save //- ftp : dot defines version function version() { return "20210608" } /** * Create */ function createFromJSON(o) { if (!o) { console.error("Attempting to create an undefined objects"); } if (!o.hasOwnProperty("a") || !o.a) { o.a = createLUID(); } //- fix : trim title o.t = o.t.trim() if (!o["@c"]) { o["d"] = "dot"; } o.le = getTimeAsInt(moment()); o.ct = o.le let x = persistPage(o); return o; }; //- q&d : fix dot imported from old function fixit(d) { debugger if (!d.s) { d.s = "stub" } if (!d.b) { d.b = "body" } if (!d.ct) { if (d.lu) { d.ct = d.lu } else { if (d.le) { d.ct = d.le } } } return d } function toOrbit(dot) { //- ftp : create method mindgraph.dot.toOrbit //- fix : save dots saved in sessionStorage let toOrbit = [] if (sessionStorage.toOrbit) { toOrbit = JSON.parse(sessionStorage.toOrbit) } toOrbit.push(dot) //- feat : all dots are saved in an array in sesssion storage //cirtical when clobbering sessionStorage.toOrbit = JSON.stringify(toOrbit) } function persistPage(o) { var a = o.a; try { store.save(o); } catch (e) { console.error(e); console.error(o); } return o; }; //- ftp : dot module update function update(node) { n = node try { if (record) { n.lu = getTimeAsInt(moment()); //- fix : set le to be lu n.le = n.lu if (!n.ct) { n.ct = n.le } } store.save(n); toOrbit(node) } catch (e) { console.error(e); console.error(); } // now update values // wn_page.setUpMap(); return n; }; /** * Read */ //-ftp : create isObjectempty function isObjectEmpty(obj) { if (obj === null || obj === undefined) { return true } return Object.keys(obj).length === 0; } function getMap(nocache) { if (!nocache || isObjectEmpty(map)) { return setupMap(); } return map; } function round(num) { return Math.round(num * 100) / 100 } //- ftp : transfer from mg2 stats to mindgraph.js function stats(session) { if (session) { var count = sessionStorage.length; var size = JSON.stringify(sessionStorage).length; // - ftp : display dots storage stats return round(size / 1000000) + "Mb/" + count + " = " + round(size / (count * 1000)) + 'Kb'; } var count = localStorage.length; var size = JSON.stringify(localStorage).length; // - ftp : display dots storage stats return round(size / 1000000) + "Mb/" + count + " = " + round(size / (count * 1000)) + 'Kb'; } function resetMap() { map = {} return getMap() } function byId(a) { let n = null return getNodeItem(a) } function byTitle(title, exact) { var result = byPropNew("t", title, exact)//.filter(function (n) { // return n["d"] // }); //console.log(result); return result; if (result.length > 0) { n = result[0]; } else { // console.error("byTitle " + title + " not found"); // return undefined if g.byTitle cannot find a node with given title n = undefined; } return n; } function byPropNew(prop /* propertyName */, value /* String */, exact /* boolean */) { if (exact) { return searchOneExact(setupMap().values, value, prop, []); } return searchOne(getMap().values, value, prop, []); } let byProp = byPropNew //- ftp : add method rename to m.d // going against simonyi's notation // it made sense in the day to carry higher cognitive load // so that the machines can made to do what we want // the tables have turned // we need to develop system that augments human ability // reduce congitive load // let the machine work it out // focus on the new aspect of the thing you are dealing with // indicate the wide context secondarily // this is the TrailMarks Way function rename(titleDot, newTitle) { let matches = byTitle(titleDot, true) if (matches.length > 1) { alert("Match is not unique. renaming first occurance ") } const oldDot = matches[0] //https://www.samanthaming.com/tidbits/70-3-ways-to-clone-objects/ let newDot = Object.assign({}, oldDot) //- ftp : change title only if dot with new title exists matches = byTitle(newTitle, true) if (matches.length > 0) { newDot.a = oldDot.a + '' newDot.t = newTitle + '' } else { newDot.t = newTitle let doRename = confirm("Would you like to rename\n " + oldDot.t + " as\n " + newDot.t + "?") if (doRename) { save(newDot) } else { delete newDot.a newDot = createFromJSON(newDot) } } return newDot.a } //- fn : make dot with given provenance you own function ownIt(titleOfExistingDot, provenanceLength) { provenance = povenance || 2 let matches = byTitle(titleDot, true) if (matches.length > 1) { alert("Match is not unique. renaming first occurance ") } const oldDot = matches[0] let newDot = oldDot newDot.t = oldDot.t.substring(0, -2) save(newDot) return newDot.a } function searchOneExact(nodes, q1, prop, acc) { nodes.forEach(function (o) { // console.log(o[prop]); if (o[prop] === q1) { acc.push( o ); } }); return acc; } // issue - Setup dot property matching regexp filter function searchOne(nodes, q1, prop, acc) { var textFilter = filterWithRegExp(prop, q1) return nodes.filter(textFilter); } function filterWithRegExp(prop, text) { text = text.replace(/\|/g, "\\|") //- fix : allow parenthesis in title text = text.replace(/\(/g, "\\(") text = text.replace(/\)/g, "\\)") var regexp = new RegExp(text, "i"); return function (o) { return o[prop] ? regexp.test(o[prop]) : false } } //- fn : beforeDate function beforeDate(date, chrono) { const dateMoment = moment(date, "YYYYMMDD") let maxTime = getTimeAsInt(dateMoment) let list = getMap().values; list = list.filter(function (dot) { try { if (dot.le) { return parseInt(dot.le) < maxTime } if (dot.lu) { return parseInt(dot.lu) < maxTime } } catch (e) { console.log(e, dot) return false } }) let list2 = list.sort(function (a, b) { let ta = 0 let tb = 0 if (a.le) ta = parseInt(a.le) if (a.lu) { ta = parseInt(a.lu) } if (b.le) { tb = parseInt(b.le) } if (b.lu) { tb = parseInt(b.lu) } let result = (ta - tb) return result }) if (chrono) { list2 = list2.reverse() } console.log(list2) return list2 } //-fn : afterDate added function afterDate(date, chrono) { const dateMoment = moment(date, "YYYYMMDD") let minTime = getTimeAsInt(dateMoment) let list = getMap().values; list = list.filter(function (dot) { try { if (dot.le) { return parseInt(dot.le) > minTime } if (dot.lu) { return parseInt(dot.lu) > minTime } } catch (e) { console.log(e, dot) return false } }) let list2 = list.sort(function (a, b) { let ta = 0 let tb = 0 if (a.le) ta = parseInt(a.le) if (a.lu) { ta = parseInt(a.lu) } if (b.le) { tb = parseInt(b.le) } if (b.lu) { tb = parseInt(b.lu) } let result = (ta - tb) return result }) if (chrono) { list2 = list2.reverse() } console.log(list2) return list2 } //- ftp : add byDaysBefore function byDaysBefore(days, chrono) { let daybefore = moment().subtract(days, 'days') let minTime = getTimeAsInt(daybefore) let list = getMap().values; debugger console.log(list) list = list.filter(function (dot) { debugger try { if (dot.lu) { return parseInt(dot.lu) > minTime } if (dot.le) { return parseInt(dot.le) > minTime } } catch (e) { console.log(e, dot) return false } }) debugger let list2 = list.sort(function (a, b) { let ta = minTime let tb = minTime console.log(moment(a.le * 1000).format("MMDD hh:mm"), a.t) if (a.le) { ta = parseInt(a.le) } if (a.lu) { ta = parseInt(a.lu) } if (b.le) { tb = parseInt(b.le) } if (b.lu) { tb = parseInt(b.lu) } let result = (ta - tb) return result }) if (chrono) { list2 = list2.reverse() } console.log(list2) return list2 } //- fn : add byDaysAfter function byDaysAfter(days, chrono) { let daybefore = moment().subtract(days, 'days') debugger let minTime = getTimeAsInt(daybefore) let list = getMap().values; list = list.filter(function (dot) { try { if (dot.le) { return parseInt(dot.le) > minTime } if (dot.lu) { return parseInt(dot.lu) > minTime } } catch (e) { console.log(e, dot) return false } }) let list2 = list.sort(function (a, b) { let ta = 0 let tb = 0 if (a.le) ta = parseInt(a.le) if (a.lu) { ta = parseInt(a.lu) } if (b.le) { tb = parseInt(b.le) } if (b.lu) { tb = parseInt(b.lu) } let result = (ta - tb) return result }) if (chrono) { list2 = list2.reverse() } console.log(list2) return list2 } function getTimeAsInt(moment) { return parseInt(moment.format("X")); } /** * Utilities */ function createLUID() { return createUUID().substring(0, 8); } function createUUID() { return getRandomAlpha() + b(0); } function getRandomAlpha() { var a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return a[Math.round((Math.random() * 25))]; } //https://gist.github.com/982883 function b(a) { return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, b); } function getTimeAsInt(moment) { return parseInt(moment.format("X")); } //- ftp : add view function view(a) { let n = byId(a) function card() { console.log(n.t) } return { card } } return { version, createFromJSON, fixit, toOrbit, byId, byTitle, byProp, rename, setFocus, getFocus, save, update, getMap, stats, beforeDate, afterDate, byDaysBefore, byDaysAfter, view } } /** * Capabilities needed to manipulate HTML as HyperMedia to do #TrailMarking * */ function mark() { function trigger(options) { alert(options) } return { trigger } } /** * * @returns Local Forage Module */ function localForageModule() { const dbName = 'MG4' //- create : localforage table dots const dots = localforage.createInstance({ name: dbName, storeName: 'dots', description: 'localforage table dots in MG4 for mindgraph v4' }) //- create : localforage table dotprops const dotProps = localforage.createInstance({ name: dbName, storeName: 'dotsProps', description: 'localforage table props in MG4 for mindgraph v4' }) return { dots, dotProps } } /** * LinkoGraphy for MindGraph * Manage Links outside the Dot */ function linkogModule() { function outLinks(a) { console.log('outlinks') } function inLinks() { console.log('inlinks') } return { outLinks, inLinks } } /** * - ftp, create : Storage Module in MindGraph */ function storageModule() { /** * Find large dots */ function find$largeDots(po = { limit: 10000 }) { // get all dots let dotsWithLargeBodies = dot.getMap().values. // filter dots with body length greater than limit filter((d) => d.b ? d.b.length > po.limit : false). // concatenate anchor, title and body map((d) => d.a + ' ' + d.t + d.b) //- ftp : sort large dots // sort by size of returned info .sort((a, b) => a.length - b.length) // compute total size let totalSize = dotsWithLargeBodies.reduce((a, b) => { return a + b.length }, 0) // return total size and array of dots with large bodies return { total: totalSize, dotsWithLargeBodies } } return { find$largeDots } } /** * Load/Merge from archive */ function archiveModule() { //- feat : store bodies in localforage for /** * * @param {*} dots */ function merge$dots(dots) { // save all dots from archive without dots or large images // into session storage dots.map(dot => { console.log(dot.t) try { if (dot.a) { let loaded = JSON.stringify(dot) sessionStorage[dot['a']] = loaded } } catch (e) { console.log(e) } } ) let versioned = {} Object.keys(sessionStorage).map(key => { // get from local storage let dot = mindgraph.dot.byId(key) if (dot) { versioned[key] = sessionStorage[key] } else { localStorage[key] = sessionStorage[key] } }) sessionStorage.versioned = JSON.stringify(versioned) } return { merge$dots } } /** * Rag bag of methods of general or not yet understood utility */ function utilsModule() { //- feat : munge Mail Body experimental function mungeMailBody() { let result = "" $('#target div').each(function (i, x) { debugger result = result + `
  • ${$(x).text()}
  • ` }) return result } function doSlides(dot) { let threads = "" function splitULs(x) { let lis = [] let sections = [] let skip = false let children = $(x).children().toArray() children.forEach(function (xx, i) { if (i < children.length - 1 && children[i + 1].tagName === 'UL') { let section = `

    ${$(xx).html()}

    ${$(children[i + 1]).html()}
    ` sections.push(section) skip = true } else { if (!skip) { lis.push($(xx).html()) } } }) return { lis, sections } } let heads = $('div>ul>li', '
    ' + dot.b + '
    ').toArray() let bodies = $('div>ul>ul', '
    ' + dot.b + '
    ').toArray() bodies.forEach(function (x, i) { let items = $('li', x).html() let bits = splitULs(x) let head = heads.length > i && heads[i] ? heads[i].innerHTML : "" let s = `

    ${head}

    ${bits.lis}` threads = threads + `
    ${s}
    ${bits.sections}
    ` console.log(heads[i], s) }) console.log(heads, bodies) threads = threads.replace(/ ,` debugger console.log(imgForTab) $('title').html('⏍' + dot.t) //- web - how : set favicon dynamically $('link[rel="icon"]').attr('href', dot.i) return threads } //- ftp : getAuthor function getAuthor(dotVa) { let dot = mindgraph.dot.byId(dotVa) if (dot && dot.b) { return dot.b } else { return dotVa } } //- ftp : u.getURLSearchParamsAsObject function getURLSearchParamsAsObject(searchString) { if (searchString.startsWith('?')) { searchString = searchString.substring(1) } let o = {} new URLSearchParams(searchString).forEach((value, key) => o[key] = value) return o } function formatTime(le, lu, ct) { let timeAsString = le; if (lu) { timeAsString = lu } if (!timeAsString) { return "?" } let orig = "" if (ct && ct > 0) { orig = moment(parseInt(ct) * 1000).format("YYYY MMM") } let data = orig + ' ' + moment(parseInt(timeAsString) * 1000).format("YYYY MMM D") return data; } function oldDots(days, fn) { mg3.dot.getMap().values. filter(function (d) { return d.le < parseInt(moment().subtract(days, 'days').format("X")) / 1000 }).map(fn) } /** * Switch localIP * Old TrailMarks from conceptipedia projects runs on localIP address * part of the security of local archiving relies on localhost in the browser * need to set localStorage.localIP to a valid value * TODO hard wired value for development at home * When swhitching router * localStorage.localIP="192.168.0.110" */ function getLocalIP() { let localIP = localStorage.localIP || "192.168.0.22" return localIP } function munge(dot) { // save src for first image to be displayed in list/compact // this way we see an image representative of the content of the dot // while in a smaller format see the saved icon for the dot // this is the favicon for saved resources // TODO whould be part of setting up map let imageSrc = "" let ref = "" try { imageSrc = $('img', '
    ' + dot.b + '
    ').first().attr("src") ref = $('a', '
    ' + dot.b + '
    ').first().attr("href") } // dots created on android may have plain text at the beginning that jquer crashes on catch (e) { console.log(dot.t, e) } dot.ref = ref ? ref : '' dot.ii = dot.i if (imageSrc) { dot.ii = imageSrc } // dot.selfLink = trailhub.dto.selfLink(dot) return dot } function findLargeDots(k) { if (!k) { k = 100 } return mindgraph.dot.getMap().values.filter(function (d) { if (d.b) { return d.b.length > k * 1000 } else { return false } }) } //- ftp : fixTitle function fixTitle(title) { let x = dot.byTitle(title) if (x.length === 1) { x = x[0] x.t = x.t.trim() // does not work //x.t = x.t.replace(/ & /g, '&') dot.save(x) } } /** * x.map(function (x) { console.log(x.t); debugger;localStorage.removeItem(x.a)}) */ return { mungeMailBody, doSlides, getAuthor, getURLSearchParamsAsObject, formatTime, oldDots, getLocalIP, munge, findLargeDots, fixTitle } } /** * - enhance : create viewHelpers in MindGraph * */ function viewHelpers() { //- fn : setBodies function setBodies() { function process(x) { debugger; let dot = mindgraph.dot.byId(x.id); if (dot.b) { $('.b', x).html(dot.b) } } debugger let x = $('.dot').toArray() x.map(process) } return { setBodies } } /** * Rag bag of mehods used in Applicative Transfers * */ function transformModule() { function debug(on) { if (on) { debugger } } return { debug } } // make dot available from console //window.dot = mindgraph.dot // purge opidox from mindgraph in TrailNext /** * Dead Wood opidox.transform = mindgraph.transform opidox.utils = mindgraph.utils window.opidox = opidox */